//========================================================================
//  This software is free: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License Version 3,
//  as published by the Free Software Foundation.
//
//  This software is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public License
//  Version 3 in the file COPYING that came with this distribution.
//  If not, see <http://www.gnu.org/licenses/>.
//========================================================================
/*!
\file    pointing_engine.ch
\brief   C++ Interface: PointingEngine
\author  Joydeep Biswas, (C) 2011
*/
//========================================================================

#include <stdio.h>
#include <vector>

#ifdef _WIN32
#define NOMINMAX
#include <windows.h>
#include "MSR_NuiApi.h"
#include "MSRKinectAudio.h"
#include "MSR_NuiImageCamera.h"
#include "MSR_NuiProps.h"
#include "MSR_NuiSkeleton.h"
#include "MSRKinectAudio.h"
#include "NuiImageBuffer.h"
#else
#include "kinect_stubs.h"
#endif

#if defined(min) || defined(max)
#undef min
#undef max
#endif

#include "timer.h"
#include "geometry.h"
#include "Eigen/Dense"
#include "Eigen/Eigenvalues"

#ifndef POINTING_ENGINE_H
#define POINTING_ENGINE_H

using namespace std;
using namespace Eigen;

#define GET_KINECT_COORDS(p) (p).z,(p).x,(p).y

class PointingEngine{
  /// 0=1D display, 1=2D display
  int displayMode;
  ///0=none, 1-3=calibrate specified point
  int calibrationState; 
  ///points 0-2 are calibrated, 3 derived by completing the parallelogram
  vector3d calibrationPoints[4];  // point0 is the origin (bottom left hand corner), point1 the bottom right hand corner, point 2 the top left hand corner
  double conditionNumbers[3];
  //Calibration of point p is the solution of A*p=B
  Matrix3d A, Ainv, I;
  Vector3d B, p;
  vector<vector3d> dataPoints;  /// pointing source locations for calibration of current point
  vector<vector3d> dataVectors; /// pointing vectors for calibration of current point
  int lastUserNum;
  vector3d firstElbowLoc;
  vector3d firstPointingVector;
  double hoverDuration;
  double hoverStart;
  double timeoutStart;
  unsigned int qualityFlags; /// one or more NUI_SKELETON_QUALITY flags combined by a bitwise-or
  bool userFound; ///Indicates whether a user was found or not (but not neccesarily tracked)
  bool userTracked; ///Indicates whether the detected user is being tracked or not
  vector3d displayNormal; /// Normal to the 2D display plane
  vector3d v1, v2; /// Basis vectors which define the display
  vector3d origin; /// Origin of display coordinate system (lower left corner)
  vector3d target3d;
  vector2d target;
  vector3d elbowLoc, shoulderLoc, handLoc, pointingVector; /// Latest tracked skeleton joint locations
  bool userSwitched; /// Indicates that the user being tracked in the current frame is different from that in the last frame
  
  ///Parameters for skeleton filtering
  static const double MaxElbowMovement;
  static const double MinCosineMove;
  static const double MinCalibrationHover;
  
public:
  /// Start calibration of point specified (1-3)
  void startCalibration(int pointNum);
  /// Stop calibration
  void stopCalibration();
  /// Observe a skeleton frame, as generated by the MS Kinect SDK
  void observe(NUI_SKELETON_FRAME* skeletons);
  /// Get back calibration details (location of point, condition number) of all points (1-4)
  void getCalibration(vector3d _calibrationPoints[4], double _conditionNumbers[3], vector< vector3d >& _dataPoints, vector< vector3d >& _dataVectors);
  /// Get target location on the display which the user is pointing at right now, 
  ///  along with how long he/she has been hovering around that location
  bool getPointingTarget(vector2d& _target, vector3d &_target3d, double hoverDuration);

  PointingEngine();
  ~PointingEngine();
  void updatePointingTarget();
  void recomputeCalibration();
  void saveCalibration();
  bool loadCalibration();
};

#endif //POINTING_ENGINE_H